package com.easylinkin.linkappapi.device.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.linkappapi.common.service.CommonService;
import com.easylinkin.linkappapi.common.utils.DateUtil;
import com.easylinkin.linkappapi.common.utils.NumberUtil;
import com.easylinkin.linkappapi.common.utils.excel.ExcelConstant;
import com.easylinkin.linkappapi.common.utils.excel.ExcelTools;
import com.easylinkin.linkappapi.common.utils.io.OutputStreamUtil;
import com.easylinkin.linkappapi.device.constant.DeviceConstant.GrainSize;
import com.easylinkin.linkappapi.device.constant.DeviceConstant.ModelCategoryEnum;
import com.easylinkin.linkappapi.device.dao.WaterRecordsMapper;
import com.easylinkin.linkappapi.device.dto.EnvironmentalAreaDTO;
import com.easylinkin.linkappapi.device.entity.Device;
import com.easylinkin.linkappapi.device.entity.DeviceModel;
import com.easylinkin.linkappapi.device.entity.WaterRecords;
import com.easylinkin.linkappapi.device.entity.vo.WaterRecordsVo;
import com.easylinkin.linkappapi.device.entity.vo.WaterStatisticsVo;
import com.easylinkin.linkappapi.device.mapper.EnvironmentalAreaMapper;
import com.easylinkin.linkappapi.device.service.DeviceService;
import com.easylinkin.linkappapi.device.service.WaterRecordsService;
import com.easylinkin.linkappapi.device.util.DeviceUtil;
import com.easylinkin.linkappapi.openapi.dto.DatapushDTO;
import com.easylinkin.linkappapi.openapi.service.DataPushService;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

/**
 * WaterRecords表服务实现类
 *
 * @author CodeGenerator
 * @date 2022/06/27
 */
@Slf4j
@Service("appWaterRecordsService")
public class WaterRecordsServiceImpl extends ServiceImpl
        <WaterRecordsMapper, WaterRecords> implements WaterRecordsService {
    @Resource
    private CommonService commonService;
    @Resource
    private LinkappUserContextProducer linkappUserContextProducer;
    @Resource
    private DeviceService deviceService;
    @Resource
    private EnvironmentalAreaMapper environmentalAreaMapper;
    @Resource
    private DataPushService dataPushService;

    @Override
    public boolean saveOne(WaterRecords appWaterRecords) {
        commonService.setCreateAndModifyInfo(appWaterRecords);
        appWaterRecords.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        return save(appWaterRecords);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateOne(WaterRecords appWaterRecords) {
        Assert.notNull(appWaterRecords.getId(), "id不能为空");
        commonService.setModifyInfo(appWaterRecords);
        appWaterRecords.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        return updateById(appWaterRecords);
    }

    @Override
    public IPage<WaterRecords> selectPage(Page page, WaterRecordsVo appWaterRecords) {
        appWaterRecords.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        return baseMapper.selectPage(page, appWaterRecords);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByIds(List<Long> idList) {
        return removeByIds(idList);
    }

    @Override
    public void export(WaterRecordsVo appWaterRecords, HttpServletRequest request, HttpServletResponse
            response) {

        IPage<WaterRecords> page = selectPage(new Page(0, -1), appWaterRecords);
        List<WaterRecords> records = page.getRecords();

        String keyValue = "设备号:deviceCode,用水增量:waterIncrement,累计用水:waterTotal,止码读数:stopReading,采集时间:collectTime";
        String title = "WaterRecords导出数据";
        String fileName = title + ".xls";
        try {
            OutputStream outputStream = OutputStreamUtil
                    .getOutputStream(request, response, fileName);
            ExcelTools.exportExcel(outputStream, keyValue, records, ExcelConstant.XLS, title);
            response.flushBuffer();
            outputStream.close();
        } catch (IOException e) {
            log.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！IOException异常" + e.getMessage());
        } catch (Exception e) {
            log.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！" + e.getMessage());
        }
    }

    @Override
    public WaterRecords getOneById(Serializable id) {
        return baseMapper.getOneById(id);
    }



    @Override
    public void datapushHandler(DatapushDTO datapushDTO) {
        // 流水处理流程
        JSONObject data = datapushDTO.getData();
        String deviceCode = datapushDTO.getDevice_id();

//            Device device = new Device();
//            device.setCode(deviceCode);
//            List<Device> deviceList = deviceService.selectDevices(device);
//            if (deviceList == null || deviceList.size() <= 0) {
//                return ;
//            }
//            device = deviceList.get(0);
        //将查询逻辑放入主线程
        Device device = datapushDTO.getDevice_data_latest();
        if(!DeviceUtil.isBelongSpecifiedDeviceType(device,ModelCategoryEnum.WATTER.getDeviceTypeNameKey())){
            return;
        }
        // 当前累计流量
        // water_flux和water_flux1  做兼容 以后新水表都用water_flux
        Double flux = data.getDouble("water_flux1");
        Double waterFlux = flux != null ? flux : data.getDouble("water_flux");
        if(null == waterFlux){
            return;
        }
        waterFlux = NumberUtil.div(waterFlux,1000d,2);
        WaterRecords waterRecords = new WaterRecords();
        waterRecords.setDeviceCode(deviceCode);
        waterRecords.setTenantId(device.getTenantId());
        waterRecords.setCollectTime(new Date());
        waterRecords.setCreateTime(new Date());
        //扩展区域id
        DeviceModel deviceModel = dataPushService.getDeviceModel(deviceCode);
        if (deviceModel!=null && deviceModel.getEnvironmentalAreaId()!=null){
            waterRecords.setAreaId(deviceModel.getEnvironmentalAreaId());
        }
        waterRecords.setStopReading(waterFlux);
        // 设备用水列表 倒序
        WaterRecordsVo waterRecordsVo = new WaterRecordsVo();
        waterRecordsVo.setDeviceCode(deviceCode);
        waterRecordsVo.setTenantId(device.getTenantId());
        List<WaterRecords> list = baseMapper.findByDeviceCode(waterRecordsVo);
        if(CollectionUtil.isEmpty(list)){
            // 第一条流水记录 增量为waterFlux 止码读数也为waterFlux
            waterRecords.setWaterIncrement(0d);
            waterRecords.setWaterTotal(0d);
        }else{
            WaterRecords endFlow = list.get(0);// 最新一条 计算增量
            Double waterIncrement = 0d;
            if(waterFlux.compareTo(endFlow.getStopReading()) == 1){
                waterIncrement = NumberUtil.sub(waterFlux,endFlow.getStopReading(),2);
            }
            waterRecords.setWaterIncrement(waterIncrement);
            waterRecords.setWaterTotal(NumberUtil.add(endFlow.getWaterTotal(),waterIncrement,2));
        }
        baseMapper.insert(waterRecords);
    }

    @Override
    public WaterStatisticsVo getWaterStatistics(WaterRecordsVo waterRecordsVo) {
        WaterStatisticsVo waterStatisticsVo = new WaterStatisticsVo();
        waterStatisticsVo.setTodayWater(0.0d);
        waterStatisticsVo.setWeekWater(0.0d);
        waterStatisticsVo.setMonthWater(0.0d);
        waterStatisticsVo.setTotalWater(0.0d);
        // 获取喷淋统计数据
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        waterRecordsVo.setTenantId(tenantId);
        if(StringUtils.isBlank(waterRecordsVo.getDeviceCode())){
            // 查询所有一级区域设备
            EnvironmentalAreaDTO areaDTO = new EnvironmentalAreaDTO();
            areaDTO.setLevel(1); // 一级区域
            areaDTO.setParentId(0); // 父节点id
            areaDTO.setType(1); // 用水
            areaDTO.setTenantId(tenantId);
            List<EnvironmentalAreaDTO> deviceModels = environmentalAreaMapper.selectDeviceByArea(areaDTO);
            if(CollectionUtil.isNotEmpty(deviceModels)){
                List<String> deviceCodes = deviceModels.stream()
                    .map(EnvironmentalAreaDTO::getDeviceCode).collect(Collectors.toList());
                waterRecordsVo.setDeviceCodes(deviceCodes);
            }
        }
        if(StringUtils.isNotEmpty(waterRecordsVo.getDeviceCode())||CollectionUtil.isNotEmpty(waterRecordsVo.getDeviceCodes())){
            // 累计用水量
            Double totalWater = baseMapper.getSumWaterByTime(waterRecordsVo);
            if(null != totalWater){
                waterStatisticsVo.setTotalWater(totalWater);
            }
            waterRecordsVo.setStartTime(DateUtil.getCurrentDayMixDate());
            waterRecordsVo.setEndTime(DateUtil.getCurrentDayMaxDate());
            // 今日用水量
            Double todayWater = baseMapper.getSumWaterByTime(waterRecordsVo);
            if(null != todayWater){
                waterStatisticsVo.setTodayWater(todayWater);
            }
            // 本周用电量
            waterRecordsVo.setStartTime(DateUtil.getCurrentDayMixDate(DateUtil.getNowWeekMonday(new Date())));
            waterRecordsVo.setEndTime(DateUtil.getCurrentDayMaxDate(new Date()));
            Double weekWater = baseMapper.getSumWaterByTime(waterRecordsVo);
            if(null != weekWater){
                waterStatisticsVo.setWeekWater(weekWater);
            }
            // 本月用电量
            waterRecordsVo.setStartTime(DateUtil.getCurrentDayMixDate(DateUtil.getNowMonthFirstDay(new Date())));
            waterRecordsVo.setEndTime(DateUtil.getCurrentDayMaxDate(new Date()));
            Double monthWater = baseMapper.getSumWaterByTime(waterRecordsVo);
            if(null != monthWater){
                waterStatisticsVo.setMonthWater(monthWater);
            }
            //获取最新的记录
            WaterRecords waterRecords = baseMapper.getLatestRecord(waterRecordsVo);
            if(waterRecords!=null){
                waterStatisticsVo.setCollectTime(waterRecords.getCollectTime());
            }
        }
        return waterStatisticsVo;
    }

    @Override
    public List<WaterStatisticsVo> getWaterStatisticsList(WaterRecordsVo waterRecordsVo) {
        waterRecordsVo.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        List<Date> dates = DateUtil
            .checkDateIsLegal(waterRecordsVo.getGrainSize(),
            DateUtil.format(waterRecordsVo.getStartTime(),DateUtil.DATE_TIME_FORMAT_DEFAULT),
            DateUtil.format(waterRecordsVo.getEndTime(),DateUtil.DATE_TIME_FORMAT_DEFAULT));

        if(CollectionUtil.isEmpty(dates)){
            return null;
        }

        if(GrainSize.DAY.equals(waterRecordsVo.getGrainSize())){
            return getDayData(dates,waterRecordsVo);
        }
        if(GrainSize.WEEK.equals(waterRecordsVo.getGrainSize())){
            return getWeekData(dates,waterRecordsVo);
        }
        if(GrainSize.MONTH.equals(waterRecordsVo.getGrainSize())){
            return getMonthData(dates,waterRecordsVo);
        }

        return null;
    }

    private List<WaterStatisticsVo> getDayData(List<Date> days, WaterRecordsVo waterRecordsVo) {
        Date startTime = days.get(0);
        Date endTime = days.get(days.size() - 1);
        waterRecordsVo.setStartTime(startTime);
        waterRecordsVo.setEndTime(endTime);
        // 初始化
        List<WaterStatisticsVo> dayList = new ArrayList<>(days.size());
        FastDateFormat format = FastDateFormat.getInstance("yyyyMMdd");
        // 查询统计
        List<WaterStatisticsVo> result = baseMapper.getWaterStatisticsVoByDay(waterRecordsVo);
        // 初始化数据
        Map<String,WaterStatisticsVo> resultMap = result.stream().collect(Collectors
            .toMap(WaterStatisticsVo::getDays, Function
                .identity(), (k1, k2) -> k1));
        for(Date date : days){
            String day = format.format(date);
            WaterStatisticsVo vo = null;
            if(null != resultMap && resultMap.containsKey(day)){
                vo = resultMap.get(day);
            }else{
                vo = new WaterStatisticsVo();
                vo.setDays(day);
                vo.setTodayWater(0.0d);
            }
            dayList.add(vo);
        }
        if(CollectionUtil.isEmpty(dayList)){
            return null;
        }
        for(WaterStatisticsVo p:dayList){
            Date day = DateUtil.parse(p.getDays(),"yyyyMMdd");
            String refDate = DateUtil.format(day,DateUtil.DATE_TIME_FORMAT_DAY);
            p.setRefDate(refDate);
        }
        return dayList;
    }

    private List<WaterStatisticsVo> getWeekData(List<Date> weekEndDays, WaterRecordsVo waterRecordsVo) {
        Date startTime = DateUtil.findMondayBySunDay(weekEndDays.get(0));
        Date endTime = weekEndDays.get(weekEndDays.size() - 1);
        waterRecordsVo.setStartTime(startTime);
        waterRecordsVo.setEndTime(endTime);
        // 初始化
        List<WaterStatisticsVo> weekList = new ArrayList<>(weekEndDays.size());
        // 查询统计
        List<WaterStatisticsVo> result = baseMapper.getWaterStatisticsVoByDay(waterRecordsVo);
        // 初始化数据
        Map<String,WaterStatisticsVo> resultMap = result.stream().collect(Collectors.toMap(WaterStatisticsVo::getWeeks, Function
            .identity(), (k1, k2) -> k1));
        for(Date date : weekEndDays){
            String week = null;
            try {
                week = DateUtil.getWeekOfYear(date);
            } catch (Exception e) {
                e.printStackTrace();
            }
            WaterStatisticsVo vo = null;
            if(null != resultMap && resultMap.containsKey(week)){
                vo = resultMap.get(week);
            }else{
                vo = new WaterStatisticsVo();
                vo.setWeeks(week);
                vo.setWeekWater(0.0d);
            }
            weekList.add(vo);
        }
        if(CollectionUtil.isEmpty(weekList)){
            return null;
        }
        for(WaterStatisticsVo p:weekList){
            String refDate = p.getWeeks();
            refDate = DateUtil.getTimeIntervalByWeek(refDate);
            p.setRefDate(refDate);
        }
        return weekList;

    }

    private List<WaterStatisticsVo> getMonthData(List<Date> monthEndDays, WaterRecordsVo waterRecordsVo) {
        Date startTime = DateUtil.findFirstDayOfMonth(monthEndDays.get(0));
        Date endTime = monthEndDays.get(monthEndDays.size() - 1);
        waterRecordsVo.setStartTime(startTime);
        waterRecordsVo.setEndTime(endTime);
        // 初始化
        List<WaterStatisticsVo> monthList = new ArrayList<>(monthEndDays.size());
        FastDateFormat format = FastDateFormat.getInstance("yyyyMM");
        // 查询统计
        List<WaterStatisticsVo> result = baseMapper.getWaterStatisticsVoByDay(waterRecordsVo);
        // 初始化数据
        Map<String,WaterStatisticsVo> resultMap = result.stream().collect(Collectors
            .toMap(WaterStatisticsVo::getMonths, Function
                .identity(), (k1, k2) -> k1));
        for(Date date : monthEndDays){
            String month = format.format(date);
            WaterStatisticsVo vo = null;
            if(null != resultMap && resultMap.containsKey(month)){
                vo = resultMap.get(month);
            }else{
                vo = new WaterStatisticsVo();
                vo.setMonths(month);
                vo.setMonthWater(0.0d);
            }
            monthList.add(vo);
        }
        if(CollectionUtil.isEmpty(monthList)){
            return null;
        }
        for(WaterStatisticsVo p:monthList){
            String refDate = p.getMonths();
            refDate = DateUtil.getTimeIntervalByMonth(refDate);
            p.setRefDate(refDate);
        }
        return monthList;
    }
}

